/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END


#include <cstdlib>
#include <cstdio>
#include <string>
#include "RsBlock.h"

using namespace std;

extern "C" {
  RSBLOCK_EXPORT
  POA_CF::Resource *
  CreateRsBlockDecoder (const char * identifier)
  {
    RsBlockDecoder * decoder;

    if (RsBlockDecoder::Create (decoder, identifier) != success) {
      return 0;
    }

    return (POA_CF::Resource *) decoder;
  }
}

status
RsBlockDecoder::Create (RsBlockDecoder *& handle,
                        const char * identifier)
{
  RsBlockDecoder * Temp = new RsBlockDecoder (identifier);

  if (!Temp) {
    handle = 0;
    return fail;
  }

  handle = Temp;
  return success;
}

RsBlockDecoder::RsBlockDecoder (const char * identifier)
  : CF__Resource_impl (identifier),
    m_threadrunning (false),
    m_bStop (false)
{
  unsigned int input_buffer_size;
  unsigned int output_buffer_size;

  // Initialize buffer(s).
  input_buffer_size = 210; // Each input block is 105.
  m_input = new char[input_buffer_size];

  output_buffer_size = 72; // Each output block is 72.
  m_output = new char[output_buffer_size];

  m_mac_output = new short[1];

  // Define data ports.
  m_rsblockin = new RsBlockInPort_impl (input_buffer_size);
  m_rsblockout = new RsBlockOutPort_impl (m_output, output_buffer_size);

  // Define MAC interfaces.
  m_mac_err_out = 
    new MAC_Ctrl_OutPort_impl<SDR_FM3TR::MAC_Signaling::MAC_ErrorControl_ReceiveLocal> ();
  m_mac_err_out2 = 
    new MAC_Ctrl_OutPort_impl<SDR_FM3TR::MAC_Signaling::MAC_ErrorControl_ReceiveLocal> ();
}

RsBlockDecoder::~RsBlockDecoder ()
{
  delete m_input;
  delete m_output;

  delete m_rsblockin;
  delete m_rsblockout;

  delete m_mac_err_out;  // Output to MAC_Receive.
  delete m_mac_err_out2; // Output to FileOutput_MAC_LLC.

  delete m_gf;
  delete m_decMsg;
}

status
RsBlockDecoder::Init ()
{
#ifdef MCS_VERBOSE
  std::cout << "RsBlockDecoder: Initializing." << std::endl;
#endif

  srand (time(0));
  m_numPackets = 0;
  m_errorThreshold = 9;

  // Block Prim Poly p(D) = 1 + 0D^1 + 0D^2 + 1D^3 + 0D^4 + 0D^5 + 0D^6 + 1D^7.
  unsigned int block_prim_poly[8] = { 1, 0, 0, 1, 0, 0, 0, 1 };

  // Initialize the parameters.
  m_n = 105;
  m_k = 72;
  m_N = 127;
  m_K = 94;
  m_m = 7;
  m_b = 1;

  m_gf = new galois::GaloisField (m_m, block_prim_poly);
  m_decMsg = new galois::GaloisFieldPolynomial (m_gf, m_N-1);

  if (((m_n-m_k)%2) == 0) {
    m_t = (m_n - m_k) / 2;
  }
  else {
    m_t = (m_n - m_k - 1) / 2;
  }

  return success;
}

void
RsBlockDecoder::RunWorker (void * param)
{
  RsBlockDecoder * component =
    (RsBlockDecoder *) param;

#ifdef MCS_VERBOSE
  std::cout << "RsBlockDecoder: Component is running." << std::endl;
#endif

  component->RunLoop();

#ifdef MCS_VERBOSE
  std::cout << "RsBlockDecoder: Component is stopping." << std::endl;
#endif
}

status
RsBlockDecoder::RunLoop ()
{
  m_bStop = false;

  if (Init () != success) {
    return fail;
  }

  while (!m_bStop) {
    Run ();
  }

  m_threadrunning = false;
  m_thread.detach ();
  return success;
}

void
RsBlockDecoder::SendFeedback (SDR_FM3TR::MAC_Signaling::MAC_Signal_Ack ack)
{

#ifdef MCS_VERBOSE
  std::cout << "RsBlockDecoder: Sending ";

  if (ack == SDR_FM3TR::MAC_Signaling::ACK) {
    std::cout << "ACK";
  }
  else {
    std::cout << "NAK";
  }

  std::cout << "." << std::endl;
#endif

  // Send MAC error control feedback.
  SDR_FM3TR::MAC_Signaling::MAC_ErrorControl_ReceiveLocal_ptr 
    remote_port, remote_port2;

  remote_port = m_mac_err_out->getRemotePort ();
  remote_port2 = m_mac_err_out2->getRemotePort ();

  switch (ack) {
  case SDR_FM3TR::MAC_Signaling::ACK:
    if (m_mac_err_out->IsConnected ()) {
      remote_port->signalACK ();
    }

    if (m_mac_err_out2->IsConnected ()) {
      remote_port2->signalACK ();
    }

    break;

  case SDR_FM3TR::MAC_Signaling::NAK:
    if (m_mac_err_out->IsConnected ()) {
      remote_port->signalNAK ();
    }

    if (m_mac_err_out2->IsConnected ()) {
      remote_port2->signalNAK ();
    }

    break;

  default:
    assert(!"ERROR: RsBlockDecoder::SendFeedback, Unknown MAC ACK type");
  }
}

void
RsBlockDecoder::start ()
  throw (CORBA::SystemException,
	 CF::Resource::StartError)
{
  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start ();
}

void
RsBlockDecoder::stop ()
  throw (CORBA::SystemException,
	 CF::Resource::StopError)
{
  m_bStop = true;
}

CORBA::Object_ptr
RsBlockDecoder::getPort (const CORBA::Char * name)
  throw (CORBA::SystemException,
	 CF::PortSupplier::UnknownPort)
{
  string temp (name);

  if (temp == "RsBlockDecoderIn") {
    return m_rsblockin->getPort ();
  }
  else if (temp == "RsBlockDecoderOut") {
    return m_rsblockout->getPort ();
  }
  else if (temp == "MAC_ErrCtrl_Out") {
    return m_mac_err_out->getPort ();
  }
  else if (temp == "MAC_ErrCtrl_Out2") {
    return m_mac_err_out2->getPort ();
  }
  else {
    throw CF::PortSupplier::UnknownPort();
  }
}

status
RsBlockDecoder::Run ()
{
  unsigned int i;
  unsigned int m_input_count;

  // Get data from input buffer.
  m_input_count = m_rsblockin->GetData (&m_input);

  if (m_input_count == 0) {
    return fail;
  }
  else if (m_input_count < m_n) {
    // Not enough data in buffer; wait for more.
    m_rsblockin->ReleaseDataBuffer (0);
    return fail;
  }

#ifdef MCS_VERBOSE
  std::cout << "RsBlockDecoder: Received " << m_input_count << " samples."
	    << std::endl;
#endif

  unsigned int m_output_count = 0;
  unsigned int m_samples_read = 0;

  while (m_input_count >= m_n) {
    // Initialize the m_gf polynomial with the incoming buffer.
    for (i=0; i<m_n; i++) {
      (*m_decMsg)[i] = m_input[m_samples_read+i];
    }

    /*
     * Optionally, add errors to the signal.
     */

    galois::GaloisFieldPolynomial blockErrors (m_gf, (m_N)-1);
    unsigned int numRandErrors = (unsigned int) rand() % 12;

    /*
     * Do not introduce errors on first packet (this is the header
     * and it must be received correctly).
     */

    if (m_numPackets == 0) {
      numRandErrors = 0;
    }

#ifdef MCS_VERBOSE
    if (numRandErrors) {
      std::cout << "RsBlockDecoder: Introducing "
		<< numRandErrors << " errors into packet "
		<< m_numPackets << "." << std::endl; 
    }
#endif

    for (i=0; i<m_N-1; i++) {
      blockErrors[i] = 0;
    }

    for (i=0; i<numRandErrors; i++) {
      unsigned int index = rand() % m_n;
      blockErrors[index] = rand() % 126 + 1;
    }

    // Add error to signal.
    (*m_decMsg) = (*m_decMsg) + blockErrors;

    /*
     * Calculate syndromes.
     */

    // Initialize the syndrome vector with zeros.
    galois::GaloisFieldPolynomial S_block (m_gf, m_n - m_k - 1);
    bool syndromeZeroFlag = false;

    for (i=0; i < m_n-m_k; i++) {
      S_block[i] = (*m_decMsg) (m_gf->alpha (m_b+i));
      syndromeZeroFlag = syndromeZeroFlag || (S_block[i].poly() == 0);
    }

    /*
     * Calculate error locator L(x) and magnitude W(x) polynomials.
     *
     * Do this step only if Syndrome vector is non-zero:
     * If last element of syndrome vector (polynomial form) is zero,
     * then skip finding error locator and magnitude polynomials.
     */

    if (syndromeZeroFlag) {
      for (i=0; i<m_k; i++) {
	m_output[i] = m_input[m_samples_read + i + m_n - m_k];
      }

#ifdef MCS_VERBOSE
  std::cout << "RsBlockDecoder: Sending " << m_k << " samples."
	    << std::endl;
#endif

      m_rsblockout->pushData (m_output, m_k);

      m_input_count -= m_n;
      m_samples_read += m_n;
      m_output_count += m_k;

      // Send ACK
      SendFeedback (SDR_FM3TR::MAC_Signaling::ACK);
      continue;
    }

    /*
     * There is a bug with the galois library, such that is one of the
     * two polynomials is of degree 1, then the polynomial division does
     * not return anything.  Therefore, both the numerator and denominator
     * polynominals are multiplied by 'x' for the time being.  This has
     * no effect on the remainder and the quotient, and is basically
     * adding a 0 to both the denominator and the numerator.
     */

    galois::GaloisFieldPolynomial A_1 (m_gf, 2*m_t);
    galois::GaloisFieldPolynomial A_2 (m_gf, m_n - m_k - 1);

    galois::GaloisFieldPolynomial D_1 (m_gf, 2*m_t);
    galois::GaloisFieldPolynomial D_2 (m_gf, 2*m_t);

    // Dividend set to x^2t initially.
    // A_1: {0,0,1}

    for (i=0; i<2*m_t; i++) {
      A_1[i] = 0;
    }

    A_1[2*(m_t)]  = 1;

    // A_2 = Syndromes
    for (i=0; i < m_n-m_k; i++) {
      A_2[i]  = S_block[i]; 
    }

    // Initialize D polynomials.  D_1 is already set to all zeros.
    D_2[0] = 1;

    unsigned int degree = 2*m_t;

    galois::GaloisFieldPolynomial Q_temp,R_temp, D_temp;

    while (degree >= m_t) {
      /*
       * The line below is the polynomial division and the most 
       * cycle-intensive part of the algorithm.
       */

      Q_temp = A_1 / A_2;
      R_temp = A_1 % A_2;

      // Update A_1 and A_2.
      A_1 = A_2;
      A_2 = R_temp;

      // Get the polynomial degree of R_temp, to check for the while loop.
      degree = R_temp.deg ();

      // Update the D polynomial.
      D_temp = (Q_temp*D_2) + D_1;

      // Update D_1 and D_2.
      D_1 = D_2;
      D_2 = D_temp;
    }

    D_2.simplify();

    if (D_2[0] != 0) {
      if (D_2.deg() <= (m_t+1) && D_2.deg() >= 1 && A_2.deg() <= m_t) {
	// W polynomial from A_2.
	A_2 = A_2 / D_2[0];

	// We've got the L polynomial from D_2.
	D_2 = D_2 / D_2[0];
      }
      else {
	// We have a failure in decoding the signal.
	D_2 = (const galois::GFSymbol) 0;
	A_2 = (const galois::GFSymbol) 0;
      }
    }
    else {
      if (D_2.deg()<=(m_t+1) && D_2.deg()>=1 && A_2.deg()<=m_t) {
	// Do nothing.
      }
      else {
	// We have a failure in decoding the signal.
	D_2 = (const galois::GFSymbol) 0;
	A_2 = (const galois::GFSymbol) 0;
      }
    }

    /*
     * Calculate the error locations.
     */

    unsigned int * errorLocations = new unsigned int [m_N];
    unsigned int numLocations = 0;

    /*
     * Solve the error locator polynomial (by chien search).  To find the
     * error locator polynomial roots, basically try all of the alpha
     * values one by one. This is a trivial search since one needs to
     * try `n` number of alpha values (from alpha^0 ... alpha^(n-1)).
     *
     * L is evaluated at [alpha^(-1) ... alpha^(-14) alpha^(-15)].
     * Zeros give the roots of the L (error locations).
     *
     * For N>n (Shortened codes), if an error location is greater than
     * n, it should be interpreted as a decoding error.
     */

    for (i=1;i<m_N+1;i++) {
      if (D_2(m_gf->alpha((m_N)-i)) == 0) {
	errorLocations[numLocations] = i;
	numLocations++;
      }
    }

    /*
     * Solve the error magnitudes, and correct the errors (using
     * Euclidean Algorithm).  For euclidean algorithm formulation,
     * check out:
     *   http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
     *   http://www.dsprelated.com/showmessage/22571/1.php
     */

    // First, obtain L'(X_j^(-1)) = L_1 + L_3*X_j^(-2) + L_5*X_j^(-4) + ...
    galois::GaloisFieldPolynomial L_prime (m_gf, D_2.deg());

    // Remove the constant term.
    for (i=0; i<D_2.deg(); i++) {
      L_prime[i] = D_2[i+1];
    }

    // Set even terms to zero.
    for (i=1; i<L_prime.deg()+1; i+=2) { 
      L_prime[i] = 0;
    }

    L_prime.simplify();

    galois::GaloisFieldElement errorVal (m_gf, 0);
    galois::GaloisFieldPolynomial blockDecoderOut = *m_decMsg;

    for (i=0;i<numLocations;i++) {
      if (L_prime (m_gf->alpha (m_N - errorLocations[i])) != 0) {
	errorVal  = m_gf->alpha ((errorLocations[i]*(1-m_b)) % m_N);
	errorVal *= A_2 (m_gf->alpha (m_N - errorLocations[i]));
	errorVal /= L_prime (m_gf->alpha (m_N - errorLocations[i]));
	blockDecoderOut[errorLocations[i] % m_N] += errorVal;
      }
      else {
	// Decoding of the codeword failed.
      }
    }

    for (i=0; i < m_k; i++) {
      m_output[i] = (char) blockDecoderOut[i + m_n - m_k].poly();
    }

    /*
     * There is some logic here to insert a data error in order to 
     * test the MAC Error Control logic.  This should only be done
     * if our MAC Error Control Output ports are connected.
     */

    if (m_mac_err_out->IsConnected() || m_mac_err_out2->IsConnected()) {
#ifdef MCS_VERBOSE
      std::cout << "RsBlockDecoder: Detected " << numLocations << " errors."
		<< std::endl;
#endif

      if (numLocations >= m_errorThreshold) {
	// Send NAK.

	for (unsigned int jj=0; jj<m_k; jj++) {
	  m_output[jj] = (char) 48; // force packet to string of "0"s
	}

#ifdef MCS_VERBOSE
	std::cout << "RsBlockDecoder: Signaling NAK." << std::endl;
#endif
	SendFeedback (SDR_FM3TR::MAC_Signaling::NAK);
      }
      else {
	// Send ACK
	SendFeedback (SDR_FM3TR::MAC_Signaling::ACK);
      }
    }

#ifdef MCS_VERBOSE
  std::cout << "RsBlockDecoder: Sending " << m_k << " samples."
	    << std::endl;
#endif

    m_rsblockout->pushData (m_output, m_k);

    m_input_count -= m_n;
    m_samples_read += m_n;
    m_output_count += m_k;
    delete errorLocations;
  } // while (m_input_count >= m_n);

  m_rsblockin->ReleaseDataBuffer (m_samples_read);
  m_numPackets++;
  return success;
} // End of RsBlockDecoder::Run()
